#include "StdAfx.h"
#include "WMPHook.h"
#include "..\WMP\Common.h"
#include "WMPPlusCore.h"
#include "WMPPlusHookData.h"
#include "WMPPlus.h"
#include "AVISplitter.h"

#pragma unmanaged

typedef HRESULT (WINAPI *PSHOpenFolderAndSelectItems)(PCIDLIST_ABSOLUTE pidlFolder, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD dwFlags);
typedef HRESULT (WINAPI *PCoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID FAR* ppv);
typedef LRESULT (WINAPI *PCallWindowProc)(WNDPROC lpPrevWndFunc, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
typedef LRESULT (WINAPI *PDispatchMessage)(const MSG *lpMsg);
typedef HMENU (WINAPI *PLoadMenu)(HINSTANCE hInstance, LPCWSTR lpMenuName);
typedef INT_PTR (WINAPI *PDialogBoxParam)(HINSTANCE hInstance, LPCWSTR lpTemplateName, HWND hWndParent, DLGPROC lpDialogFunc, LPARAM dwInitParam);
typedef LONG_PTR (WINAPI *PSetWindowLong)(HWND hWnd, int nIndex, LONG_PTR dwNewLong);
typedef HRESULT (WINAPI *PMFCreateMediaSession)(IMFAttributes* pConfiguration, IMFMediaSession** ppMediaSession);
typedef HRESULT (WINAPI *PMFCreatePMPMediaSession)(DWORD dwCreationFlags, IMFAttributes *pConfiguration, IMFMediaSession** ppMediaSession, IMFActivate **ppEnablerActivate);
typedef PTP_TIMER (WINAPI *PCreateThreadpoolTimer)(PTP_TIMER_CALLBACK pfnti, PVOID pv, PTP_CALLBACK_ENVIRON pcbe);
typedef VOID (WINAPI *PCloseThreadpoolTimer)(PTP_TIMER pti);
typedef DWORD (WINAPI *PGetTickCount)();
typedef HRESULT (WINAPI *PDllGetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID *ppv);

PSHOpenFolderAndSelectItems pSHOpenFolderAndSelectItems;
PCoCreateInstance pCoCreateInstance;
PCallWindowProc pCallWindowProc;
PDispatchMessage pDispatchMessage;
PLoadMenu pLoadMenu;
PDialogBoxParam pDialogBoxParam;
PSetWindowLong pSetWindowLong;
PMFCreateMediaSession pMFCreateMediaSession;
PMFCreatePMPMediaSession pMFCreatePMPMediaSession;
PCreateThreadpoolTimer pCreateThreadpoolTimer;
PCloseThreadpoolTimer pCloseThreadpoolTimer;
PGetTickCount pGetTickCount;
PDllGetClassObject pQuartzGetClassObject;

HRESULT WINAPI WMPSHOpenFolderAndSelectItems(PCIDLIST_ABSOLUTE pidlFolder, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD dwFlags) {
	if ((int)cidl < 0 || !pidlFolder) return E_UNEXPECTED;
	if ((int)cidl > 0 && !apidl) return E_UNEXPECTED;
	HRESULT hr;
	try {
		try {
			if (!ResolveMediaFolder(pidlFolder, cidl, apidl))
				return S_OK;
		} catch(...) {
			TraceException(_T("WMPSHOpenFolderAndSelectItems"));
		}
	} finally (
		hr = (*pSHOpenFolderAndSelectItems)(pidlFolder, cidl, apidl, dwFlags);
	)
	return hr;
}

HRESULT WINAPI WMPCoCreateInstance(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID FAR* ppv) {
	return (*pCoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv);
}

bool IsWMPlayer(HWND hWnd) {
	CreateTextBuffer(classBuffer);

	return (GetClassName(hWnd, classBuffer, classBufferLength) &&
		lstrcmpi(classBuffer, _T("WMPlayerApp")) == 0);
}

LRESULT WINAPI WMPCallWindowProcW(WNDPROC lpPrevWndFunc, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
	try {
		if (Msg == WM_CLOSE && IsWMPlayer(hWnd))
			Core->Hook->setWMPlayerClosing(true);
	} catch(...) {
		TraceException(_T("WMPCallWindowProcW"));
	}
	return (*pCallWindowProc)(lpPrevWndFunc, hWnd, Msg, wParam, lParam);
}

LRESULT WINAPI WMPDispatchMessageW(const MSG *lpMsg) {
	LRESULT res;
	if (lpMsg && lpMsg->message == WM_TIMER && IsWMPlayer(lpMsg->hwnd)) {
		Core->Hook->setFD(true);
		try {
			res = (*pDispatchMessage)(lpMsg);
		} finally (
			Core->Hook->setFD(false);
		)
	} else {
		res = (*pDispatchMessage)(lpMsg);
	}
	return res;
}

HMENU WINAPI WMPLoadMenuW(HINSTANCE hInstance, LPCWSTR lpMenuName) {
	HMENU hMenu = (*pLoadMenu)(hInstance, lpMenuName);
	DWORD error = GetLastError();
	try {
		if (hMenu && (INT_PTR)lpMenuName == 616 && GetModuleHandle(_T("wmploc.dll")) == hInstance)
			if (!Core->Hook->getMainMenu())
				Core->Hook->setMainMenu(hMenu);
	} catch(...) {
		TraceException(_T("WMPLoadMenuW"));
	}
	SetLastError(error);
	return hMenu;
}

INT_PTR WINAPI WMPDialogBoxParamW(HINSTANCE hInstance, LPCWSTR lpTemplateName, HWND hWndParent, DLGPROC lpDialogFunc, LPARAM dwInitParam) {
	return (*pDialogBoxParam)(hInstance, lpTemplateName, hWndParent, lpDialogFunc, dwInitParam);
}

HRESULT WINAPI WMPMFCreateMediaSession(IMFAttributes* pConfiguration, IMFMediaSession** ppMediaSession) {
	return (*pMFCreateMediaSession)(pConfiguration, ppMediaSession);
}

HRESULT WINAPI WMPMFCreatePMPMediaSession(DWORD dwCreationFlags, IMFAttributes *pConfiguration, IMFMediaSession** ppMediaSession, IMFActivate **ppEnablerActivate) {
	return (*pMFCreatePMPMediaSession)(dwCreationFlags, pConfiguration, ppMediaSession, ppEnablerActivate);
}

HRESULT WINAPI WMPQuartzGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) {
	CreateTextBuffer(textBuffer);
	CreatePathBuffer(idBuffer);

	if (IsEqualCLSID(rclsid, CLSID_AviSplitter)) {
		if (IsEqualIID(riid, IID_IClassFactory)) {
			IClassFactory* org;
			HRESULT res = (*pQuartzGetClassObject)(rclsid, IID_IClassFactory, (void**)&org);
			if (SUCCEEDED(res))
				res = AVISplitterFactory::Create(org, riid, ppv);
			return res;
		} else {
			_tcscpy(textBuffer, _T("WARNING: Unknown AviSplitter factory interface query "));
			LPOLESTR id;
			if (SUCCEEDED(StringFromIID(riid, &id))) {
				_tcscat(textBuffer, FromLPWSTR(id, idBuffer, idBufferLength));
				CoTaskMemFree(id);
			}
			OutputDebugString(textBuffer);
		}
	}
	return (*pQuartzGetClassObject)(rclsid, riid, ppv);
}

void WINAPI WMPSetPrevAddress(LPCSTR method, void* proc) {
	try {
		if (lstrcmpiA("SHOpenFolderAndSelectItems", method) == 0)
			pSHOpenFolderAndSelectItems = (PSHOpenFolderAndSelectItems)proc;
		else if (lstrcmpiA("CoCreateInstance", method) == 0)
			pCoCreateInstance = (PCoCreateInstance)proc;
		else if (lstrcmpiA("CallWindowProcW", method) == 0)
			pCallWindowProc = (PCallWindowProc)proc;
		else if (lstrcmpiA("DispatchMessageW", method) == 0)
			pDispatchMessage = (PDispatchMessage)proc;
		else if (lstrcmpiA("LoadMenuW", method) == 0)
			pLoadMenu = (PLoadMenu)proc;
		else if (lstrcmpiA("DialogBoxParamW", method) == 0)
			pDialogBoxParam = (PDialogBoxParam)proc;
#ifdef _WIN64
		else if (lstrcmpiA("SetWindowLongPtrW", method) == 0)
#else
		else if (lstrcmpiA("SetWindowLongW", method) == 0)
#endif
			pSetWindowLong = (PSetWindowLong)proc;
		else if (lstrcmpiA("MFCreateMediaSession", method) == 0)
			pMFCreateMediaSession = (PMFCreateMediaSession)proc;
		else if (lstrcmpiA("MFCreatePMPMediaSession", method) == 0)
			pMFCreatePMPMediaSession = (PMFCreatePMPMediaSession)proc;
		else if (lstrcmpiA("CreateThreadpoolTimer", method) == 0)
			pCreateThreadpoolTimer = (PCreateThreadpoolTimer)proc;
		else if (lstrcmpiA("CloseThreadpoolTimer", method) == 0)
			pCloseThreadpoolTimer = (PCloseThreadpoolTimer)proc;
		else if (lstrcmpiA("GetTickCount", method) == 0)
			pGetTickCount = (PGetTickCount)proc;
		else if (lstrcmpiA("QuartzGetClassObject", method) == 0)
			pQuartzGetClassObject = (PDllGetClassObject)proc;
	} catch(...) {
		TraceException(_T("WMPSetPrevAddress"));
	}
}
